﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;
using SLRT.business.src;
using Emgu.CV;

namespace SLRT.dao.data
{
    public class DBImage
    {
        public static String SIGNATURE = "<Sign Language Recognition and Translation database image file>";
        public static int N_ANGLES = 4;
        public static int DB_IMAGE_WIDTH = 320;
        public static int DB_IMAGE_HEIGHT = 240;
        public static double DB_IMAGE_ASPECT_RATIO = 1.333;
        public static int CONTOUR_POWER = 2;
        public static double WHITE_PROPORTION = 0.04;
        public static int HV_AREA_LIMIT = 200;
        public static int HV_THINNESS = 5;
        public static double RATIO_THINNESS = 8.0;
        public static int OUTER_TRIES = 10;
        public static int INNER_TRIES = 10;
        public static int THIN_AREA_LOWER_LIMIT = 150;
        public static int THIN_AREA_HIGHER_LIMIT = 250;

        public static int AAA = 10;

        private bool[,] raster;
        private double[] angles;
        private int shapeHeight;
        private int shapeWidth;
        private int leftShapeCenter;
        private int topShapeCenter;

        private Shape greatestShape;
        private Shape leftShape;
        private Shape rightShape;
        private Shape topShape;
        private Shape bottomShape;
        private Shape[] thinShapes;
        private double[] thinRatios;

        public DBImage(bool[,] raster, int shapeWidth, int shapeHeight, int leftShapeCenter, int topShapeCenter, double[] angles) 
        {
            this.raster = raster;
            this.angles = angles;
            this.shapeHeight = shapeHeight;
            this.shapeWidth = shapeWidth;
            this.leftShapeCenter = leftShapeCenter;
            this.topShapeCenter = topShapeCenter;
        }

        public bool[,] Raster
        {
            get { return raster; }
        }

        public double[] Angles
        {
            get { return angles; }
        }

        public int LeftShapeCenter
        {
            get { return leftShapeCenter; }
        }

        public int TopShapeCenter
        {
            get { return topShapeCenter; }
        }

        public int ShapeWidth
        {
            get { return shapeWidth; }
        }

        public int ShapeHeight
        {
            get { return shapeHeight; }
        }

        public Shape GreatestShape
        {
            get { return greatestShape; }
            set { greatestShape = value; }
        }

        public Shape LeftShape
        {
            get { return leftShape; }
            set { leftShape = value; }
        }

        public Shape RightShape
        {
            get { return rightShape; }
            set { rightShape = value; }
        }

        public Shape TopShape
        {
            get { return topShape; }
            set { topShape = value; }
        }

        public Shape BottomShape
        {
            get { return bottomShape; }
            set { bottomShape = value; }
        }

        public Shape[] ThinShapes
        {
            get { return thinShapes; }
            set { thinShapes = value; }
        }

        public double[] ThinRatios
        {
            get { return thinRatios; }
            set { thinRatios = value; }
        }

        /*
        * Structure:
        * - signature
        * - width
        * - height
        * - shapeWidth
        * - shapeHeight
        * - leftShapeCenter
        * - topShapeCenter
        * - 5 angles
        * - raster image
        */

        public void saveToDisk(FileInfo file) 
        {
            int wi = this.raster.GetLength(0);
            int he = this.raster.Length;
            int x, y;
            int power = 0;
            int b = 0;

            StreamWriter dos;

            try {

                dos = new StreamWriter(file.OpenWrite());

                dos.Write(SIGNATURE);                                       //dos.writeBytes(SIGNATURE); ---- java    
                                                                            //maye dos.WriteLine ?
                dos.Write(wi);
                dos.Write(he);
                dos.Write(this.shapeWidth);
                dos.Write(this.shapeHeight);
                dos.Write(this.leftShapeCenter);
                dos.Write(this.topShapeCenter);

                for (int i = 0; i < this.angles.Length; i++)
                {
                    dos.Write(this.angles[0]);
                }
                
                y = 0;
            
                while (y < he) 
                {
                    x = 0;
                    
                    while (x < wi)
                    {
                        switch (power) 
                        {
                            case 0:
                                if (this.raster[y,x]) 
                                {
                                    b |= 1;
                                }
                            break;
                        
                            case 1:
                                if (this.raster[y,x]) 
                                {
                                    b |= 2;
                                }
                            break;

                            case 2:
                                if (this.raster[y,x]) 
                                {
                                    b |= 4;
                                }
                            break;

                            case 3:
                                if (this.raster[y,x]) 
                                {
                                    b |= 8;
                                }
                            break;
                        
                            case 4:
                                if (this.raster[y,x]) 
                                {
                                    b |= 16;
                                }
                            break;
                        
                            case 5:
                                if (this.raster[y,x]) 
                                {
                                    b |= 32;
                                }
                            break;
                        
                            case 6:
                                if (this.raster[y,x]) 
                                {
                                    b |= 64;
                                }
                            break;
                        
                            case 7:
                                if (this.raster[y,x]) 
                                {
                                    b |= 128;
                                }
                            break;
                        }

                        power++;
        
                        if (power > 7) 
                        {
                            dos.Write(b);       // dos.writeByte(b) ------ java
                            power = 0;
                            b = 0;
                        }
                
                        x++;
                    }
                    
                    y++;
                }

                dos.Close();
            } 
            
            catch (Exception ex) 
            {
                Console.WriteLine(ex.Message);
            }
        }

        private static int ReadInt(BinaryReader reader)
        {
            byte[] value = reader.ReadBytes(4);
            Array.Reverse(value);
            return BitConverter.ToInt32(value, 0);
        }

        public static DBImage loadFromDisk(FileInfo file) 
        {
            Int32 wi, he;
            bool[,] boolIm = null;
            
            //byte[] bSignature = new byte[SIGNATURE.Length];
            char[] bSignature = new char[SIGNATURE.Length];
            
            
            //DataInputStream dis;
            BinaryReader dis;

            int x, y;
            int power = 0;
            long b = 0, q = 0;

            Int32 shapeWidth = 0;
            Int32 shapeHeight = 0;
            Int32 leftShapeCenter = 0;
            Int32 topShapeCenter = 0;
            double[] angles = new double[N_ANGLES];

            try 
            {
                dis = new BinaryReader(file.OpenRead());

                dis.Read(bSignature, 0, SIGNATURE.Length);

                if (!(new String(bSignature)).Equals(SIGNATURE))
                {
                    return null;
                }

                wi = ReadInt(dis);
                he = ReadInt(dis);
                shapeWidth = ReadInt(dis);
                shapeHeight = ReadInt(dis);
                leftShapeCenter = ReadInt(dis);
                topShapeCenter = ReadInt(dis);
                
                //for (int i = 0; i < N_ANGLES; i++) 
                //{
                    //angles[i]= dis.Read();
                //}
                
                boolIm = new bool[he,wi];

//                for (int i = 0; i < he; i++)
//                    boolIm[i] = new bool[wi];

                b = dis.ReadByte();
                y = 0;
            
                while (y < he) 
                {
                    x = 0;
                    
                    while (x < wi) 
                    {
                        if (power > 7) 
                        {
                            power = 0;
                            b = dis.ReadByte();
                        }
                        
                        switch (power) 
                        {
                            case 0:
                                q = (b & 1);
                            break;
                            
                            case 1:
                                q = (b & 2);
                            break;
                            
                            case 2:
                                q = (b & 4);
                            break;
                            
                            case 3:
                                q = (b & 8);
                            break;
                        
                            case 4:
                                q = (b & 16);
                            break;
                        
                            case 5:
                                q = (b & 32);
                            break;
                        
                            case 6:
                                q = (b & 64);
                            break;
                            
                            case 7:
                                q = (b & 128);
                            break;
                        }

                        if (q > 0) 
                        {
                            boolIm[y,x] = true;
                        } 
                        else 
                        {
                            boolIm[y,x] = false;
                        }

                        power++;
                        x++;
                    }
                    
                    y++;
                }

                dis.Close();
            } 
            catch (Exception ex) 
            {
                Console.WriteLine(ex.Message);
            }

            DBImage dbIm = new DBImage(boolIm, shapeWidth, shapeHeight, leftShapeCenter, topShapeCenter, angles);
            
            return dbIm;
        }

        public static DBImage loadRawFromDisk(FileInfo file) 
        {
            IImage rawIm = null;
            DBImage dbIm = null;
        
            int leftMostCoord;
            int topMostCoord;
            int leftShapeCenter;
            int topShapeCenter;
            int shapeWidth;
            int shapeHeight;
            int[,] resizedGrayIntIm;
            int[,] contourIntIm;
            int[] histogram;
            int treshold;
            int dbImWidth, dbImHeight;
            
            double ar;
        
            Shape leftShape;
            Shape rightShape;
            Shape topShape;
            Shape bottomShape;
            Shape greatestShape;
        
            Point? leftMostPoint;
            Point? rightMostPoint;
            Point? bottomMostPoint;
            Point? topMostPoint;
 
            GrayImageAndHistogram contourIntImAndHistogram;
        
            bool[,] boolIm;
            bool[,] boolIm2;
            
            try 
            {
                rawIm = (IImage)Image.FromFile(file.Name);
            } 
            catch (IOException ex) 
            {
                Console.WriteLine(ex.Message);
            }

            ar = (double) rawIm.Bitmap.Width / rawIm.Bitmap.Height;
        
            if (ar < 1.333) 
            {
                dbImWidth = (int) Math.Round(DB_IMAGE_WIDTH * ar);
                dbImHeight = DB_IMAGE_HEIGHT;
            } 
            else 
            {
                dbImWidth = DB_IMAGE_WIDTH;
                dbImHeight = (int) Math.Round(DB_IMAGE_HEIGHT / ar);
            }

            int topLeftX = 10;
            int topLeftY = 10;
            int bottomRightX = 10;
            int bottomRightY = 10;
            contourIntIm = null;
            histogram = null;
            resizedGrayIntIm = ImageAlgorithms.toResizedGreyscaleMatrix(rawIm, topLeftX, topLeftY, bottomRightX, bottomRightY, dbImWidth, dbImHeight);
            ImageAlgorithms.toContour(resizedGrayIntIm, CONTOUR_POWER, ref contourIntIm, ref histogram);
            treshold = ImageAlgorithms.computeNecessaryThreshold(WHITE_PROPORTION, histogram, dbImWidth * dbImHeight);
            boolIm = ImageAlgorithms.toBoolImage(contourIntIm, treshold);
            boolIm2 = (bool[,])boolIm.Clone();
            greatestShape = ImageAlgorithms.findGreatestShape(boolIm2);

            leftMostPoint = greatestShape.LeftMostPoint;
            rightMostPoint = greatestShape.RightMostPoint;
            bottomMostPoint = greatestShape.BottomMostPoint;
            topMostPoint = greatestShape.TopMostPoint;

            leftShape = ImageAlgorithms.reduceShapeHVAreaLimit(greatestShape, leftMostPoint, HV_THINNESS, true, HV_AREA_LIMIT);
            rightShape = ImageAlgorithms.reduceShapeHVAreaLimit(greatestShape, rightMostPoint, HV_THINNESS, true, HV_AREA_LIMIT);
            topShape = ImageAlgorithms.reduceShapeHVAreaLimit(greatestShape, topMostPoint, HV_THINNESS, false, HV_AREA_LIMIT);
            bottomShape = ImageAlgorithms.reduceShapeHVAreaLimit(greatestShape, bottomMostPoint, HV_THINNESS, false, HV_AREA_LIMIT);

            leftMostCoord = leftMostPoint.Value.X;
            topMostCoord = topMostPoint.Value.Y;
            leftShapeCenter = leftShape.getCenter().Value.X;
            topShapeCenter = topShape.getCenter().Value.Y;

            shapeWidth = rightShape.getCenter().Value.X - leftShapeCenter;
            shapeHeight = bottomShape.getCenter().Value.Y - topShapeCenter;

            dbIm = new DBImage(boolIm, shapeWidth, shapeHeight, leftShapeCenter, topShapeCenter, null);
            
            return dbIm;
        }

        public static DBImage getInstanceFromRawMemory(IImage rawIm) 
        {
            DBImage dbIm = null;
            
            int leftMostCoord;
            int topMostCoord;
            int leftShapeCenter;
            int topShapeCenter;
            int shapeWidth;
            int shapeHeight;
            int[,] resizedGrayIntIm;
            int[,] contourIntIm;
            int[] histogram;
            int treshold;
            int dbImWidth, dbImHeight;

            double ar;
        
            Shape leftShape;
            Shape rightShape;
            Shape topShape;
            Shape bottomShape;
            Shape greatestShape;
        
            Point? leftMostPoint;
            Point? rightMostPoint;
            Point? bottomMostPoint;
            Point? topMostPoint;
            
            GrayImageAndHistogram contourIntImAndHistogram;
        
            bool[,] boolIm;
        
            ar = (double) rawIm.Bitmap.Width / rawIm.Bitmap.Height;
        
            if (ar < 1.333) 
            {
                dbImWidth = (int) Math.Round(DB_IMAGE_HEIGHT * ar);
                dbImHeight = DB_IMAGE_HEIGHT;
            } 
            else 
            {
                dbImWidth = DB_IMAGE_WIDTH;
                dbImHeight = (int) Math.Round(DB_IMAGE_WIDTH / ar);
            }


            int topLeftX = 10;
            int topLeftY = 10;
            int bottomRightX = 10;
            int bottomRightY = 10;
            contourIntIm = null;
            histogram = null;
            resizedGrayIntIm = ImageAlgorithms.toResizedGreyscaleMatrix(rawIm, topLeftX, topLeftY, bottomRightX, bottomRightY, dbImWidth, dbImHeight);
            ImageAlgorithms.toContour(resizedGrayIntIm, CONTOUR_POWER, ref contourIntIm, ref histogram);
            treshold = ImageAlgorithms.computeNecessaryThreshold(WHITE_PROPORTION, histogram, dbImWidth * dbImHeight);
            boolIm = ImageAlgorithms.toBoolImage(contourIntIm, treshold);
            greatestShape = ImageAlgorithms.findGreatestShape(boolIm);

            Shape[] thinShapes = ImageAlgorithms.findThinShapes(greatestShape, N_ANGLES, OUTER_TRIES, INNER_TRIES, RATIO_THINNESS, THIN_AREA_LOWER_LIMIT, THIN_AREA_HIGHER_LIMIT);
            
            double[] angles = {0.0, 0.0, 0.0, 0.0, 0.0};
            double[] thinRatios = {1.0, 1.0, 1.0, 1.0, 1.0};
            
            if ((thinShapes != null)) 
            {
                //angles = new double[5];
                for (int i = 0; i < thinShapes.Length; i++) 
                {
                    if (thinShapes[i] != null) 
                    {
                        angles[i] = thinShapes[i].getElongation();
                        thinRatios[i] = thinShapes[i].getAspectRatio();
                    }
                }
            }

            leftMostPoint = greatestShape.LeftMostPoint;
            rightMostPoint = greatestShape.RightMostPoint;
            bottomMostPoint = greatestShape.BottomMostPoint;
            topMostPoint = greatestShape.TopMostPoint;

            leftShape = ImageAlgorithms.reduceShapeHVAreaLimit(greatestShape, leftMostPoint, HV_THINNESS, true, HV_AREA_LIMIT);
            rightShape = ImageAlgorithms.reduceShapeHVAreaLimit(greatestShape, rightMostPoint, HV_THINNESS, true, HV_AREA_LIMIT);
            topShape = ImageAlgorithms.reduceShapeHVAreaLimit(greatestShape, topMostPoint, HV_THINNESS, false, HV_AREA_LIMIT);
            bottomShape = ImageAlgorithms.reduceShapeHVAreaLimit(greatestShape, bottomMostPoint, HV_THINNESS, false, HV_AREA_LIMIT);

            leftMostCoord = leftMostPoint.Value.X;
            topMostCoord = topMostPoint.Value.Y;
            leftShapeCenter = leftShape.getCenter().Value.X;
            topShapeCenter = topShape.getCenter().Value.Y;

            shapeWidth = rightShape.getCenter().Value.X - leftShapeCenter;
            shapeHeight = bottomShape.getCenter().Value.Y - topShapeCenter;

            dbIm = new DBImage(boolIm, shapeWidth, shapeHeight, leftShapeCenter, topShapeCenter, angles);

            /*
            * Not necessary, but there.
            */
            
            dbIm.GreatestShape = greatestShape;
            dbIm.LeftShape = leftShape;
            dbIm.RightShape = rightShape;
            dbIm.TopShape = topShape;
            dbIm.BottomShape = bottomShape;
            dbIm.ThinShapes = thinShapes;
            dbIm.ThinRatios = thinRatios;
            
            return dbIm;
        }
    }
}
